package com.duguo.dynamicmvc.factory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javassist.ClassClassPath;
import javassist.ClassPool;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;

import com.duguo.dynamicmvc.controller.RootController;
import com.duguo.dynamicmvc.factory.interceptor.InterceptorGenerator;
import com.duguo.dynamicmvc.factory.processor.ProcessorHolder;
import com.duguo.dynamicmvc.factory.stage.Stage;
import com.duguo.dynamicmvc.factory.utils.BeanUtils;
import com.duguo.dynamicmvc.view.resolver.ViewResolver;

public class ControllerBuilderImpl implements BeanFactoryAware, ControllerBuilder {
    
	private static Log log = LogFactory.getLog(ControllerBuilderImpl.class);
    private List<Stage> builderStages;
    private ProcessorHolder _processorHolder;
    private Map<String, ControllerFactory> controllerFactoryBeans;
    private Map<String,Object> gloableParameterResolvers;
    private Map<String,Object> gloableParameterValidators;
    private Map<String,InterceptorGenerator> gloableInterceptorGenerators;
    private Map<String,Object> gloableInjections;
    private Class modelSuperClass;
    private boolean inited=false;
    private BeanFactory _beanFactory;
    private ViewResolver _viewResolver;

	public void build(){    	
    	if(inited)
    		return;
    	else{
    		inited=true;
    		ClassPool.getDefault().insertClassPath(new ClassClassPath(getClass()));
    		init();
    		clearBuilder();
    	}
    }

	private void init(){
    	

        long startTime = System.currentTimeMillis();
        
        initControllerFactoryBeans();
        executeStages();
        controllerFactoryBeans = null;

        log.info(String.format("Build controllers completed in %s ms",(System.currentTimeMillis() - startTime)));
        
        displayControllerMap();
    }

	private void executeStages(){
		for (Stage stage : builderStages) {
            for (ControllerFactory factory : controllerFactoryBeans.values()) {
                try {
					stage.execute(factory);
				} catch (Exception e) {
					throw new RuntimeException("Failed to execute stage "+stage.getClass().getName(),e);
				}
            }
        }
	}

	private void clearBuilder() {
		if(gloableParameterResolvers!=null){    			
			gloableParameterResolvers.clear();
			gloableParameterResolvers=null;
		}
		if(gloableParameterValidators!=null){    			
			gloableParameterValidators.clear();
			gloableParameterValidators=null;
		}
	}

	private RootController getRootController() {
		return (RootController)_beanFactory.getBean("/");
	}


    private void initControllerFactoryBeans() {
        controllerFactoryBeans = new LinkedHashMap<String, ControllerFactory>(0);
        List<String> controllerNameList = new ArrayList<String>();
        for (String beanName : ((ListableBeanFactory)_beanFactory).getBeanDefinitionNames()) {
            if (beanName.startsWith("/")) {
                controllerNameList.add(beanName);
            }
        }
        Collections.sort(controllerNameList);
        for (String beanName : controllerNameList) {
            controllerFactoryBeans.put(beanName, (AbstractControllerFactory) _beanFactory.getBean("&" + beanName));
        }
    }

    private void displayControllerMap() {
        List<String> controllerNameList = new ArrayList<String>();
        displayControllerMap(getRootController(), "", controllerNameList);
        Collections.sort(controllerNameList);
        StringBuffer controllersSB = new StringBuffer("controller mapping:\r\n");
        for (String beanName : controllerNameList) {
            controllersSB.append(beanName);
            controllersSB.append("\r\n");
        }
        log.info(controllersSB);
    }

    @SuppressWarnings("unchecked")
    private void displayControllerMap(Object controllerInstance, String currentParent, List<String> controllerNameList) {
        Map<String, Object> registedControllers = BeanUtils.tryToGetFieldValue(controllerInstance, "registedControllers", Map.class);
        if (registedControllers != null) {
            for (String controllerName : registedControllers.keySet()) {
                displayControllerMap(registedControllers.get(controllerName), currentParent + "/" + controllerName, controllerNameList);
            }
            Object dynamicController = BeanUtils.tryToGetFieldValue(controllerInstance, "dynamicController", Object.class);
            if (dynamicController != null) {
                String controllerName = _processorHolder.getControllerProcessor().getControllerNameBuilder().getControllerPath(dynamicController.getClass().getName());
                controllerName=_processorHolder.getControllerProcessor().getControllerNameBuilder().getLastLevelName(controllerName);
                displayControllerMap(dynamicController, currentParent + "/" + controllerName + "*", controllerNameList);
            }
        } else {
        	if(currentParent.endsWith("/index")){
        		currentParent=currentParent.substring(0,currentParent.length()-5);
        	}
            controllerNameList.add(currentParent);
        }
    }

    /*
      * getter and setters
      */

    public Map<String, ControllerFactory> getControllerFactoryBeans() {
        return controllerFactoryBeans;
    }

    public void setBuilderStages(List<Stage> builderStages) {
        this.builderStages = builderStages;
    }
    
    

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    	_beanFactory=beanFactory;
    }

	public Map<String, Object> getGloableParameterResolvers() {
		return gloableParameterResolvers;
	}

	public void setGloableParameterResolvers(
			Map<String, Object> gloableParameterResolvers) {
		this.gloableParameterResolvers = gloableParameterResolvers;
	}

	public Map<String, Object> getGloableParameterValidators() {
		return gloableParameterValidators;
	}

	public void setGloableParameterValidators(
			Map<String, Object> gloableParameterValidators) {
		this.gloableParameterValidators = gloableParameterValidators;
	}

	public Map<String, Object> getGloableInjections() {
		return gloableInjections;
	}

	public void setGloableInjections(Map<String, Object> gloableInjections) {
		this.gloableInjections = gloableInjections;
	}

	public Map<String, InterceptorGenerator> getGloableInterceptorGenerators() {
		return gloableInterceptorGenerators;
	}

	public void setGloableInterceptorGenerators(
			Map<String, InterceptorGenerator> gloableInterceptorGenerators) {
		this.gloableInterceptorGenerators = gloableInterceptorGenerators;
	}

	public Class getModelSuperClass() {
		return modelSuperClass;
	}

	public void setModelSuperClass(Class modelSuperClass) {
		this.modelSuperClass = modelSuperClass;
	}

	public ProcessorHolder getProcessorHolder() {
		return _processorHolder;
	}

	public void setProcessorHolder(ProcessorHolder processorHolder) {
		_processorHolder = processorHolder;
	}

	public ViewResolver getViewResolver() {
		return _viewResolver;
	}

	public void setViewResolver(ViewResolver viewResolver) {
		_viewResolver = viewResolver;
	}


}
